package com.hunliji.monitor.share.component.redis.impl;

import com.alibaba.fastjson.JSON;
import com.hunliji.common.exception.WarnException;
import com.hunliji.monitor.share.component.redis.RedisFacade;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.connection.StringRedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * Redis调用实现
 * 移除了第一版在各个方法里统一加前缀的代码-因为这种方式比较重，在做一些特殊技术方案的时候会有坑
 * 1.那么还要不要加key的前缀？
 * 要加的，因为如果不加前缀，测试和预发用一个redis,则key一样，预发有bug,会“污染线上数据”，导致线上BUG
 * 2.如何加前缀？
 * 不要在这个类的方法里统一加，而是在开发代码时根据自己的需要有意识的加前缀
 * @author wuchen
 */
@Slf4j
@Component(value = "redisFacade")
public class RedisFacadeImpl implements RedisFacade {
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    /**
     * 默认的 第几页
     */
    private static final Long DEFAULT_PAGE_NUM = 1L;

    /**
     * 默认的 每页的数量
     */
    private static final Long DEFAULT_PAGE_SIZE = 10L;

    /**
     * 默认的过期时间 单位：秒
     */
    private static final Long DEFAULT_EXPIRES = 120L;


    /**
     * 设置缓存-使用默认时间 string
     * @param key 缓存key
     * @param value 缓存key
     */
    @Override
    public void set(String key, String value) {
        set(key,value,DEFAULT_EXPIRES);
    }


    @Override
    public void set(String key, String value, long expires) {
        set(key,value,expires,TimeUnit.SECONDS);
    }

    @Override
    public void set(String key, String value, long expires, TimeUnit unit) {
        stringRedisTemplate.opsForValue().set(key, value, expires, unit);
    }

    @Override
    public void batchSet(Map<String, String> map, Long timeout) {
        RedisSerializer<String> serializer = stringRedisTemplate.getStringSerializer();
        stringRedisTemplate.executePipelined(new RedisCallback<String>() {
            @Override
            public String doInRedis(RedisConnection connection) throws DataAccessException {
                map.forEach((key, value) -> {
                    connection.set(serializer.serialize(key), serializer.serialize(value), Expiration.seconds(timeout), RedisStringCommands.SetOption.UPSERT);
                });
                return null;
            }
        }, serializer);
    }

    @Override
    public String get(String key) {
        return stringRedisTemplate.opsForValue().get(key);
    }

    @Override
    public Map<String, String> multiGet(List<String> keys) {
        Map<String,String> valueMap = new HashMap<>();
        List<Object> valueList = stringRedisTemplate.executePipelined(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                StringRedisConnection conn = (StringRedisConnection)connection;
                for (String key : keys) {
                    conn.get(key);
                    log.info("multiGet-获取数据结果,one,key:{}",key);
                }
                return null;
            }
        });
        for (int i = 0 ; i<keys.size();i++){
            if(valueList.size()<i+1){
                continue;
            }
            Object value = valueList.get(i);
            String key = keys.get(i);
            if(Objects.nonNull(value)){
                valueMap.put(key,value.toString());
            }
        }
        log.info("multiGet-获取数据结果,all,valueList:{}", JSON.toJSONString(valueList));
        return valueMap;
    }

    @Override
    public void del(String key) {
        stringRedisTemplate.delete(key);
    }



    @Override
    public void addZSet(String key, String value, Double score) {
        boolean isSuccess = stringRedisTemplate.opsForZSet().add(key,value,score);
        if(!isSuccess){
            throw new WarnException("RedisFacadeImpl-添加sortSet数据失败");
        }
    }

    @Override
    public void delZSet(String key, String value) {
        Long delNum = stringRedisTemplate.opsForZSet().remove(key,value);
        if(delNum == 0){
            throw new WarnException("删除元素发生异常");
        }
    }

    @Override
    public Set<String> getZSetByScore(String key, Double start, Double end) {
        return getZSetByScore(key,start,end,DEFAULT_PAGE_NUM,DEFAULT_PAGE_SIZE);
    }

    @Override
    public Set<String> getZSetByScore(String key, Double start, Double end, Long pageNum, Long pageSize) {
        //计算分页
        Long offset = (pageNum-1)*pageSize;
        Set<String> set = stringRedisTemplate.opsForZSet().rangeByScore(key,start,end,offset,pageSize);
        return set;
    }

    @Override
    public Set<String> getReverseZSetByScore(String key, Double start, Double end) {
        return getReverseZSetByScore(key,start,end,DEFAULT_PAGE_NUM,DEFAULT_PAGE_SIZE);
    }

    @Override
    public Set<String> getReverseZSetByScore(String key, Double start, Double end, Long pageNum, Long pageSize) {
        Long offset = (pageNum-1)*pageSize;
        return stringRedisTemplate.opsForZSet().reverseRangeByScore(key,start,end,offset,pageSize);
    }
}
